package flexo;

import java.util.ArrayList;
import java.util.Collections;

import org.lwjgl.Sys;

import flexo.common.*;
import flexo.common.System;
import flexo.common.LogMsgType;

/**
 * Main class of the library. It's the entry point of all the systems.
 * @author Miguel
 */
public class Kernel
{
	// Static Interface ////////////////////////////////////////////////
	public static final String VERSION = "0.1";
	public static boolean DEBUG = false;
	
	protected static Kernel instance = null;
	
	protected static Log log;
	
	static
	{
		if (DEBUG)
		{
			log = new Log(null);
		}
		else
		{
			log = new Log("flexo-log");
		}
	}
	
	private static boolean isLoaded()
	{
		return instance != null;
	}
	
	/**
	 * Adds a system to the kernel. Systems added are automatically
	 * deleted when Kernel.release() is called or when the last State exists.
	 */
	public static void addSystem(flexo.common.System system)
	{
		if (!isLoaded())
		{
			instance = new Kernel();
		}
		instance.systemList.add(system);
	}
	
	/**
	 * Returns the reference of a system already loaded into the Kernel with
	 * the addSystem method.
	 * @param name The name of the system you want to retrieve.
	 * @return
	 */
	public static flexo.common.System getSystem(String name)
	{
		if (isLoaded())
		{
			flexo.common.System sys = null;
			for (System s: instance.systemList)
			{
				if (s.getName().equals(name))
				{
					sys = s;
				}
			}
			
			if (sys == null)
			{
				flexo.Kernel.log("flexo.Kernel","'"+name+"' system not found",LogMsgType.ERROR);
				flexo.Kernel.error();
			}
			
			return sys;
		}
		return null;
	}	
	
	/**
	 * Sends a message to the log.
	 * @param origin The name of the origin of the message, usually a class.
	 * @param msg The text of the message.
	 * @param type the type of message.
	 */
	public static void log(String origin, String msg, LogMsgType type)
	{
		log.addLog(origin,msg,type);
	}
	
	/**
	 * Sends a STANDARD message to the log.
	 * @param origin The name of the origin of the message, usually a class.
	 * @param msg The text of the message.
	 */
	public static void log(String origin, String msg)
	{
		log.addLog(origin,msg,LogMsgType.STANDARD);
	}	
	
	/**
	 * Call it when all needed systems have been added to the Kernel
	 * and you want the engine to start the execution.
	 */
	public static void loop()
	{
		if (instance != null)
		{
			while (true)
			{
				instance.update();
			}
		}
	}
	
	/**
	 * Shutdowns the engine.
	 */
	public static void release()
	{
		if (isLoaded())
		{
			instance.cleanup();
			instance = null;
		}
	}
	
	/**
	 * Halts the system and shows the last message from the log.  
	 */
	public static void error()
	{		
		Sys.alert("Error",log.getLastMsg().replace('\t', ' '));
		release();
	}
	
	// Class Interface ///////////////////////////////////////////	
	protected flexo.common.Timer timer;
	
	protected float lastTime = 0;
	protected long lastTime2 = 0;
	protected float elapsedTime = 0;
	
	protected ArrayList<flexo.common.System> systemList;
	
	private Kernel()
	{
		log("flexo.Kernel", "Starting engine, Version: " + VERSION);
		systemList = new ArrayList<flexo.common.System>();		
		
		timer = new Timer();
		lastTime = timer.getTime();
		elapsedTime = 0;		
	}
	
	private void cleanup()
	{
		log("flexo.Kernel", "Shutting down");

		Collections.reverse(systemList);
		for (flexo.common.System s: systemList)
		{
			s.remove();
		}
		
		log("flexo.Kernel", "Exiting");
		log.close();
		java.lang.System.exit(0);
	}
	
	private void updateTimer()
	{		
		Timer.tick();
		float current = timer.getTime(); 
		elapsedTime = current - this.lastTime;
		this.lastTime = current;
	}
	
	private void update()
	{		
		// Update all systems
		for (flexo.common.System s: systemList)
		{
			s.tick(this.elapsedTime);
		}
		//Thread.yield();
		try
		{
			Thread.sleep(0);
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		updateTimer();
	}
}
